package vnu.edu;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.UngroundedException;
import jade.content.onto.basic.Action;
import jade.content.onto.basic.Result;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.SequentialBehaviour;
import jade.lang.acl.ACLMessage;
import jade.util.leap.ArrayList;
import jade.util.leap.List;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import vnu.edu.vocabulary.DatabaseAgentVocabulary;

public class NewsDatabaseAgent extends Agent implements DatabaseAgentVocabulary {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Codec codec = new SLCodec();
	private Ontology ontology = NewsOntology.getInstance();

	private Logger log = Logger.getLogger(NewsDatabaseAgent.class.getName());

	private Connection conection;
	private boolean isNewestData;
	List newsList;

	protected void setup() {
		log.info("A NewsDatabaseAgent is starting...");
		log.info("Agent name: " + getLocalName());

		// Register language and ontology
		getContentManager().registerLanguage(codec);
		getContentManager().registerOntology(ontology);

		isNewestData = false;
		newsList = new ArrayList();
		// updateData();

		SequentialBehaviour sb = new SequentialBehaviour();
		sb.addSubBehaviour(new GetDataFromDB());
		sb.addSubBehaviour(new ReceiveMessages(this));
		addBehaviour(sb);
	}

	class GetDataFromDB extends OneShotBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		@Override
		public void action() {
			// TODO Auto-generated method stub
			try {
				log.info("Connecting to database...");
				Class.forName("com.mysql.jdbc.Driver").newInstance();
				conection = DriverManager.getConnection(MYSQL_LINK, MYSQL_USER,
						MYSQL_PASSWORD);

			} catch (Exception e) {
				// TODO: handle exception
				log.info("Can not connect to database: " + e.getMessage());
				e.printStackTrace();
			}
			log.info("Connected to database");

			log.info("Updating data...");

			if (newsList.size() > 0) {
				newsList.clear();
			}

			if (getNews(TECHNOLOGY_NEWS_TABLE_NAME, 10)
					&& getNews(SPORT_NEWS_TABLE_NAME, 10)) {
				log.info("Updated all news\n" + getNewsListString());
			}

			try {
				conection.close();
				log.info("Closed database's connection");
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				log.info("Can not close database's connection" + e.getMessage());
			}
		}

	}

	class InsertRequestedInformation extends OneShotBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private String userName;

		InsertRequestedInformation(Agent a, String userRequested) {
			super(a);
			this.userName = userRequested;
		}

		@Override
		public void action() {
			// TODO Auto-generated method stub
			try {
				log.info("Connecting to database...");
				Class.forName("com.mysql.jdbc.Driver").newInstance();
				conection = DriverManager.getConnection(MYSQL_LINK, MYSQL_USER,
						MYSQL_PASSWORD);
			} catch (Exception e) {
				// TODO: handle exception
				log.info("Can not connect to database: " + e.getMessage());
				e.printStackTrace();
			}
			log.info("Connected to database");
			log.info("Inserting data...");
			if (insertRequestInformation(userName)) {
				log.info("Insert requested Infomation successfull.");
			}
			try {
				conection.close();
				log.info("Closed database's connection");
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				log.info("Can not close database's connection" + e.getMessage());
			}
		}

	}

	class ReceiveMessages extends CyclicBehaviour {

		/**
		 * 
		 */

		private static final long serialVersionUID = 1L;

		public ReceiveMessages(Agent a) {
			super(a);
		}

		@Override
		public void action() {
			// TODO Auto-generated method stub
			ACLMessage receivedMsg = receive();
			if (receivedMsg == null) {
				block();
				return;
			}
			try {
				ContentElement content = getContentManager().extractContent(
						receivedMsg);
				Concept action = ((Action) content).getAction();

				int msgPerformative = receivedMsg.getPerformative();
				switch (msgPerformative) {
				case ACLMessage.REQUEST: {
					log.info("Request from: "
							+ receivedMsg.getSender().getLocalName());

					if (action instanceof UpdateNews) {
						if (isNewestData) {
							addBehaviour(new HandleGetNewsList(myAgent,
									receivedMsg));
						} else {
							SequentialBehaviour updateForSendSB = new SequentialBehaviour();
							updateForSendSB
									.addSubBehaviour(new GetDataFromDB());
							updateForSendSB
									.addSubBehaviour(new HandleGetNewsList(
											myAgent, receivedMsg));
							addBehaviour(updateForSendSB);
						}
					} else if (action instanceof InsertRequestedInformationAction) {
						String userJustRequested = ((InsertRequestedInformationAction) action)
								.getUserRequested();
						addBehaviour(new InsertRequestedInformation(myAgent,
								userJustRequested));
					} else {
						replyNotUnderstood(receivedMsg);
					}
				}
					break;

				default: {
					log.info("Received unexpected performative msg!");
				}
					break;
				}
			} catch (UngroundedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (CodecException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (OntologyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	class HandleGetNewsList extends OneShotBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private ACLMessage request;

		HandleGetNewsList(Agent a, ACLMessage request) {
			super(a);
			this.request = request;
		}

		@Override
		public void action() {
			// TODO Auto-generated method stub
			try {
				ContentElement content = getContentManager().extractContent(
						request);
				Result result = new Result((Action) content, newsList);
				ACLMessage reply = request.createReply();
				reply.setPerformative(ACLMessage.INFORM);
				getContentManager().fillContent(reply, result);
				send(reply);
				log.info("Sending newsList...");
			} catch (UngroundedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (CodecException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (OntologyException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	private boolean getNews(String tableName, int numberOfRows) {
		boolean result = true;
		try {
			log.info("Updating data from table:" + tableName);
			Statement statement = conection.createStatement();
			String statementStr = "SELECT * FROM " + tableName + " LIMIT "
					+ numberOfRows;

			ResultSet resultSet = statement.executeQuery(statementStr);

			String newsTitle, newsDescription, newsLink;
			// Date newsDate;

			NewsElement newsElement;
			while (resultSet.next()) {
				// newsId = resultSet.getString(1);
				newsTitle = resultSet.getString(2);
				newsDescription = resultSet.getString(3);
				newsLink = resultSet.getString(4);
				// newsDate = resultSet.getDate(4);
				newsElement = new NewsElement(newsTitle, newsDescription,
						newsLink);
				newsList.add(newsElement);
			}
			isNewestData = true;
		} catch (SQLException e) {
			// TODO: handle exception
			log.info("Update false from table: " + tableName);
			log.log(Level.SEVERE, null, e);
			result = false;
			isNewestData = false;
		}
		return result;
	}

	private boolean insertRequestInformation(String userName) {
		boolean result = true;
		Integer maxRequestId = null;

		try {
			log.info("Insert request information");
			Statement statement = conection.createStatement();
			String statementStr = "SELECT MAX(" + REQUESTED_ID + ") FROM "
					+ REQUESTED_NEWS_TABLE_NAME;
			ResultSet resultSet = statement.executeQuery(statementStr);

			if (resultSet.first()) {
				maxRequestId = resultSet.getInt(1);
			} else {
				maxRequestId = 1;
			}

			Date now = new Date();
			Format dateFormater = new SimpleDateFormat("yyyy-MM-dd");
			
			int maxRequestIndex = maxRequestId.intValue();
			maxRequestIndex++;
			maxRequestId = new Integer(maxRequestIndex);
			// statementStr = "INSERT INTO " + REQUESTED_NEWS_TABLE_NAME +
			// "VALUES(" + maxRequestId.toString() + "," + "\"" + userName +
			// "\"" + "," + "\"" + "2012-04-24" + "\"" + ");";
			statementStr = "INSERT INTO " + REQUESTED_NEWS_TABLE_NAME
					+ " VALUES(" + maxRequestId.toString() + "," + "\""
					+ userName + "\"" + "," + "\"" + dateFormater.format(now) + "\""
					+ ");";
			log.info("STATEMENT:" + statementStr);
			result = statement.execute(statementStr);
		} catch (SQLException e) {
			// TODO: handle exception
			log.info("Insert false from " + REQUESTED_NEWS_TABLE_NAME
					+ " table");
			log.log(Level.SEVERE, null, e);
			result = false;
		}

		return result;
	}

	private String getNewsListString() {
		StringBuffer resultString = new StringBuffer();
		int numberOfNews = newsList.size();
		NewsElement newsElement;
		for (int i = 0; i < numberOfNews; i++) {
			newsElement = (NewsElement) newsList.get(i);
			resultString.append(newsElement.toString() + "\n");
		}
		return resultString.toString();
	}

	void replyNotUnderstood(ACLMessage msg) {
		// -----------------------------------------
		try {
			ContentElement content = getContentManager().extractContent(msg);
			ACLMessage reply = msg.createReply();
			reply.setPerformative(ACLMessage.NOT_UNDERSTOOD);
			getContentManager().fillContent(reply, content);
			send(reply);
			log.info("Not understood receivedMessage");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}
